#include "stdafx.h"
#include "FramebufferObject.h"
#include <Utilities\glext.h>
#include <Utilities\Extensions.h>
using namespace std;

FramebufferObject::FramebufferObject(void)
: _FBOObject(GenerateFBOId()), _savedFBOObject(0)
{
	//Bind this FBO so that it actually gets created now
	GuardedBind();
	GuardedUnbind();
}

FramebufferObject::~FramebufferObject(void) 
{
	glDeleteFramebuffersEXT(1, &_FBOObject);
}

void FramebufferObject::Bind(void) 
{
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _FBOObject);
}

void FramebufferObject::Disable(void) 
{
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

void FramebufferObject::AttachRenderBuffer(const GLenum attachment, const GLuint buffId)
{
	GuardedBind();

#ifndef NDEBUG
	if(GetAttachedId(attachment) != buffId)
	{
#endif
		glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, buffId);
#ifndef NDEBUG
	}
	else
	{
		cerr << "Framebuffer Object::AttachRenderBuffer PERFORMANCE WARNING:" << endl << "\tRedundant bind of RenderbufferObject (id = " << buffId << ")" << endl;
	}
#endif

	GuardedUnbind();
}

GLint FramebufferObject::GetMaxColorAttachments(void)
{
	GLint maxAttach = 0;
	glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &maxAttach);
	return maxAttach;
}

GLuint FramebufferObject::GenerateFBOId(void)
{
	GLuint id = 0;
	glGenFramebuffersEXT(1, &id);
	return id;
}

void FramebufferObject::GuardedBind(void) 
{
	//Only binds if _FBOObject is different than the currently bound FBO
	GLint savedFBOObject;
	glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &savedFBOObject);
	_savedFBOObject = static_cast<GLuint>(savedFBOObject);
	if (_FBOObject != _savedFBOObject)
	{
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _FBOObject);
	}
}

void FramebufferObject::GuardedUnbind(void) const
{
	//Returns FBO binding to the previously enabled FBO
	if (_savedFBOObject != _FBOObject)
	{
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _savedFBOObject);
	}
}

void
FramebufferObject::FramebufferTextureND(const GLenum attachment, const GLenum texType, const GLuint texId, const int mipLevel, const int layer)
{
	if (texType == GL_TEXTURE_1D)
	{
		glFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, attachment, GL_TEXTURE_1D, texId, mipLevel);
	}
	else if (texType == GL_TEXTURE_3D)
	{
		glFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, attachment, GL_TEXTURE_3D, texId, mipLevel, layer);
	}
	else if (texType == GL_TEXTURE_2D_ARRAY_EXT)
	{
		glFramebufferTextureLayerEXT(GL_FRAMEBUFFER_EXT, attachment, texId, mipLevel, layer);
	}
	else
	{
		//Default is GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB, or cube faces
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, texType, texId, mipLevel);
	}
}

bool FramebufferObject::GetIsValid(void)
{
	return GetIsValid(std::cerr);
}

bool FramebufferObject::GetIsValid(ostream& ostr)
{
#ifndef NDEBUG
	GuardedBind();

	bool isOK = false;

	GLenum status;                                            
	status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
	switch(status)
	{                                          
	case GL_FRAMEBUFFER_COMPLETE_EXT: //Everything's OK
		isOK = true;
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
		ostr << "Framebuffer Object error: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT" << endl;
		isOK = false;
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
		ostr << "Framebuffer Object error: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT" << endl;
		isOK = false;
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
		ostr << "Framebuffer Object error: GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT" << endl;
		isOK = false;
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
		ostr << "Framebuffer Object error: GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT" << endl;
		isOK = false;
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
		ostr << "Framebuffer Object error: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT" << endl;
		isOK = false;
		break;
	case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
		ostr << "Framebuffer Object error: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT" << endl;
		isOK = false;
		break;
	case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
		ostr << "Framebuffer Object error: GL_FRAMEBUFFER_UNSUPPORTED_EXT" << endl;
		isOK = false;
		break;
	default:
		ostr << "Framebuffer Object error: Unknown" << endl;
		isOK = false;
	}

	GuardedUnbind();
	return isOK;
#else //NDEBUG
	ostr;
	return true; 
#endif
}

const GLenum FramebufferObject::GetAttachedType(const GLenum attachment)
{
	//Returns GL_RENDERBUFFER_EXT or GL_TEXTURE
	GuardedBind();
	GLint type = 0;
	glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT, &type);
	GuardedUnbind();
	return GLenum(type);
}

const GLuint FramebufferObject::GetAttachedId(const GLenum attachment)
{
	GuardedBind();
	GLint id = 0;
	glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &id);
	GuardedUnbind();
	return GLuint(id);
}

const GLint FramebufferObject::GetAttachedMipLevel(const GLenum attachment)
{
	GuardedBind();
	GLint level = 0;
	glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT, &level);
	GuardedUnbind();
	return level;
}

const GLint FramebufferObject::GetAttachedCubeFace(const GLenum attachment)
{
	GuardedBind();
	GLint level = 0;
	glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT, &level);
	GuardedUnbind();
	return level;
}

const GLint FramebufferObject::GetAttachedZSlice(const GLenum attachment)
{
	GuardedBind();
	GLint slice = 0;
	glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT, &slice);
	GuardedUnbind();
	return slice;
}



